/*
 * Created by Jaren at 2021/12/30 16:19
 */
import 'dart:math';
import 'dart:typed_data';

import 'package:binary/binary.dart';
import 'package:inmotion_protocol/common/mode.dart';
import 'package:inmotion_protocol/common/vehicle_info.dart';
import 'package:inmotion_protocol/ezcan/bean/real_time_info.dart';
import 'package:inmotion_protocol/ezcan/ezcan_constant.dart';
import 'package:inmotion_protocol/lorin/constant.dart';
import 'package:inmotion_protocol/util/byte_util.dart';
import 'package:inmotion_protocol/util/version.dart';

class V5AllRealTimeInfo extends AbsAllRealTimeInfo {
  V5AllRealTimeInfo(AbsRealTimeInfo realTimeInfo, AbsRealTimeState realTimeState, AbsRealTimeError realTimeError) : super(realTimeInfo, realTimeState, realTimeError);
}

class V5RealTimeInfo extends AbsRealTimeInfo {
  Int16 dynamicSpeedLimit = Int16.zero;

  Uint16 remainderRange = Uint16.zero;

  V5RealTimeInfo.fromBytes(ProductProperty property, Uint8ClampedList bytes, [int offset = 0]) : super.fromBytes(property, bytes, offset) {
    batteryLevel = ByteUtil.toUint16(calBatteryLevelByVoltage(voltage.value));
    dynamicSpeedLimit = ByteUtil.toInt16(calDynamicSpeedLimit(property.getDefaultDesignedMaxSpeed(), voltage.value));

    remainderRange = Uint16(((batteryLevel.value / 10000) * estimatedTotalMileage.value).toInt());
  }

  /// 根据当前电压计算剩余电量
  /// 单位0.01%
  int calBatteryLevelByVoltage(int voltage) {
    if (voltage >= 8250) return 100 * 100;
    if (voltage <= 6800) return 0;
    return (((voltage - 6800) / (8250 - 6800.0)) * 100 * 100).toInt();
  }

  /// 计算动态限速
  /// 根据当前型号支持的最大速度[supportedMaxSpeed]和当前电压[voltage]
  /// [supportedMaxSpeed]单位应为0.01km/h
  int calDynamicSpeedLimit(int supportedMaxSpeed, int voltage) {
    var b = voltage / 7670;
    var dynamicSpeedLimit = (supportedMaxSpeed * (b * b)).toInt();
    return min(supportedMaxSpeed, dynamicSpeedLimit);
  }
}

class V5RealTimeState extends AbsRealTimeState {
  V5RealTimeState.fromBytes(Uint8ClampedList bytes, [int offset = 0]) : super.fromBytes(bytes, offset) {
    int mode = ByteUtil.bytes2UIntLe(bytes, 60);
    pcModeValue = ByteUtil.toUint32(mode & 0x0F);
    switch (pcModeValue.value) {
      case 0:
        pcMode = Mode.idle;
        break;
      case 1:
        pcMode = Mode.working;
        break;
      case 5: // 锁车
        pcMode = Mode.idle;
        lockState = ByteUtil.bool2Uint4(true);
        break;
      case 9:
        pcMode = Mode.powerOff;
        break;
    }
  }

  @override
  String toString() {
    return 'V5RealTimeState{pcMode: $pcMode, lightState: ${headlightState.value}, lockState: ${lockState.value}';
  }
}

class V5RealTimeError extends AbsRealTimeError {
  // 故障
  Uint4 crystalState = Uint4.zero; // 晶振异常 - 主板
  Uint4 comInitState = Uint4.zero; // 通讯初始化 - 主板
  Uint4 spiFlashState = Uint4.zero; // 外部存储 - 主板
  Uint4 motorNotCalibratedState = Uint4.zero; // 电机无校准信息 - 电机
  Uint4 rtcState = Uint4.zero; // RTC - RTC
  Uint4 comState = Uint4.zero; // 通讯异常 - 主板
  Uint4 sensorOutputState = Uint4.zero; // 传感器输出异常 - 主板
  Uint4 logSysState = Uint4.zero; // 日志系统异常 - 主板
  Uint4 batterySignalState = Uint4.zero; // 电池信号错误 - 电池
  Uint4 batteryOutputState = Uint4.zero; // 电池输出异常 - 电池
  Uint4 currentSamplingState = Uint4.zero; // 电流采样初始信息错误 - 主板
  Uint4 motorHallState = Uint4.zero; // 电机霍尔 - 电机

  // 警告
  Uint4 riskBehaviorState = Uint4.zero; // 危险骑行
  Uint4 overVoltageState = Uint4.zero; // 过压
  Uint4 overloadState = Uint4.zero; // 过载
  Uint4 overCurrentState = Uint4.zero; // 过流
  Uint4 mosOverTempState = Uint4.zero; // MOS过温
  Uint4 overPowerState = Uint4.zero; // 超过输出能力限制
  Uint4 motorBlockState = Uint4.zero; // 电机抱死

  V5RealTimeError.fromBytes(Uint8ClampedList bytes, [int offset = 0]) : super.fromBytes(bytes, 0) {
    originalValue = ByteUtil.bytes2Uint32Le(bytes, offset);
    if (originalValue.value == 0xFF) return;
    switch (originalValue.value) {
      case 17:
        crystalState = ByteUtil.bool2Uint4(true);
        break;
      case 18:
        comInitState = ByteUtil.bool2Uint4(true);
        break;
      case 19:
        spiFlashState = ByteUtil.bool2Uint4(true);
        break;
      case 21:
        motorNotCalibratedState = ByteUtil.bool2Uint4(true);
        break;
      case 22:
        rtcState = ByteUtil.bool2Uint4(true);
        break;
      case 23:
        comState = ByteUtil.bool2Uint4(true);
        break;
      case 26:
        sensorOutputState = ByteUtil.bool2Uint4(true);
        break;
      case 27:
        logSysState = ByteUtil.bool2Uint4(true);
        break;
      case 28:
        batterySignalState = ByteUtil.bool2Uint4(true);
        break;
      case 29:
        batteryOutputState = ByteUtil.bool2Uint4(true);
        break;
      case 30:
        currentSamplingState = ByteUtil.bool2Uint4(true);
        break;
      case 37:
        motorHallState = ByteUtil.bool2Uint4(true);
        break;
      case 25:
        riskBehaviorState = ByteUtil.bool2Uint4(true);
        break;
      case 31:
        overVoltageState = ByteUtil.bool2Uint4(true);
        break;
      case 34:
        overloadState = ByteUtil.bool2Uint4(true);
        break;
      case 35:
        overCurrentState = ByteUtil.bool2Uint4(true);
        break;
      case 36:
        mosOverTempState = ByteUtil.bool2Uint4(true);
        break;
      case 38:
        overPowerState = ByteUtil.bool2Uint4(true);
        break;
      case 39:
        motorBlockState = ByteUtil.bool2Uint4(true);
        break;
    }
  }

  bool hasVisibleError() {
    return crystalState.value == 1 ||
        comInitState.value == 1 ||
        spiFlashState.value == 1 ||
        motorNotCalibratedState.value == 1 ||
        rtcState.value == 1 ||
        comState.value == 1 ||
        sensorOutputState.value == 1 ||
        logSysState.value == 1 ||
        batterySignalState.value == 1 ||
        batteryOutputState.value == 1 ||
        currentSamplingState.value == 1 ||
        motorHallState.value == 1;
  }

  bool hasVisibleWarning() {
    return riskBehaviorState.value == 1 ||
        overVoltageState.value == 1 ||
        overloadState.value == 1 ||
        overCurrentState.value == 1 ||
        mosOverTempState.value == 1 ||
        overPowerState.value == 1 ||
        motorBlockState.value == 1;
  }
}

/// V8综合实时信息
/// [V8AllRealTimeInfo], [V8RealTimeInfo], [V8RealTimeState], [V8RealTimeError]仅适用于V8
/// V8S, V8F等应使用[V10LikeAllRealTimeInfo]等包含[V10Like]相关的类
class V8AllRealTimeInfo extends V5AllRealTimeInfo {
  V8AllRealTimeInfo(AbsRealTimeInfo realTimeInfo, AbsRealTimeState realTimeState, AbsRealTimeError realTimeError) : super(realTimeInfo, realTimeState, realTimeError);
}

class V8RealTimeInfo extends V5RealTimeInfo {
  V8RealTimeInfo.fromBytes(ProductProperty property, Uint8ClampedList bytes, [int offset = 0]) : super.fromBytes(property, bytes, offset);
}

class V8RealTimeState extends V5RealTimeState {
  V8RealTimeState.fromBytes(Uint8ClampedList bytes, [int offset = 0]) : super.fromBytes(bytes, offset);

  @override
  String toString() {
    return 'V8RealTimeState{pcMode: $pcMode, lightState: ${headlightState.value}, lockState: ${lockState.value}';
  }
}

class V8RealTimeError extends V5RealTimeError {
  V8RealTimeError.fromBytes(Uint8ClampedList bytes, [int offset = 0]) : super.fromBytes(bytes, offset);
}

class V10LikeAllRealTimeInfo extends AbsAllRealTimeInfo {
  V10LikeAllRealTimeInfo(
    V10LikeRealTimeInfo realTimeInfo,
    V10LikeRealTimeState realTimeState,
    V10LikeRealTimeError realTimeError,
  ) : super(realTimeInfo, realTimeState, realTimeError);
}

class V10LikeRealTimeState extends AbsRealTimeState {
  Uint4 transportationModeState = Uint4.zero;

  V10LikeRealTimeState.fromBytes(Uint8ClampedList bytes, [int offset = 0]) : super.fromBytes(bytes, offset) {
    int mode = ByteUtil.bytes2UIntLe(bytes, 60, 4);
    pcModeValue = ByteUtil.toUint32(mode & 0x0F);
    switch (pcModeValue.value) {
      case 1:
        pcMode = Mode.idle;
        break;
      case 2:
      case 3:
        pcMode = Mode.working;
        break;
      case 4:
        pcMode = Mode.powerOff;
        break;
    }
    transportationModeState = ByteUtil.bool2Uint4((mode & 0x100) == 0x100);
    lockState = ByteUtil.bool2Uint4((mode & 0x200) == 0x200);
  }

  @override
  String toString() {
    return 'V10LikeRealTimeState{${super.toString()}, transportationModeState: ${transportationModeState.value}, pcMode: $pcMode, lightState: ${headlightState.value}, lockState: ${lockState.value}';
  }
}

class V10LikeRealTimeInfo extends AbsRealTimeInfo {
  Uint16 dynamicSpeedLimit = Uint16.zero;
  Int8 motorTemp = Int8.zero;
  Int8 fanTemp = Int8.zero;

  /// 最大速度
  Int16 maxSpeed = Int16.zero;

  /// 单次能耗
  Uint32 energy = Uint32.zero;

  /// 总能耗
  Uint32 totalEnergy = Uint32.zero;

  /// 扭矩
  Int16 torque = Int16.zero;

  /// 加速度
  Int16 acceleration = Int16.zero;

  /// 剩余里程
  Uint16 remainderRange = Uint16.zero;

  V10LikeRealTimeInfo.fromBytes(ProductProperty property, Uint8ClampedList bytes, [int offset = 0]) : super.fromBytes(property, bytes, offset) {
    batteryLevel = ByteUtil.toUint16(calBatteryLevelByVoltage(voltage.value));
    dynamicSpeedLimit = ByteUtil.toUint16(calDynamicSpeedLimit(property.getDefaultDesignedMaxSpeed(), voltage.value));

    fanTemp = ByteUtil.toInt8(bytes[32] > bytes[33] ? bytes[32] : bytes[33]);
    motorTemp = ByteUtil.toInt8(bytes[34] > bytes[35] ? bytes[34] : bytes[35]);

    mileage = ByteUtil.toUint16(ByteUtil.bytes2Uint32Le(bytes, 48).value ~/ 10);

    /// 本次骑行时间
    rideTime = ByteUtil.toUint32(ByteUtil.bytes2Uint32Le(bytes, 127).value);

    /// 原始单位为0.01J，对外同一单位为0.01kj
    energy = ByteUtil.toUint32(ByteUtil.bytes2Uint32Le(bytes, 111).value ~/ 1000);
    totalEnergy = ByteUtil.toUint32(ByteUtil.bytes2Uint32Le(bytes, 103).value ~/ 1000);

    torque = ByteUtil.bytes2Int16Le(bytes, 145);

    if (VersionUtils.higherOrEquals(property.versionWrapper.getAppVersionByDevice(EzcanConstant.EUC_DEVICE_PC), VersionConstant.v10Like10200)) {
      mosTemp = fanTemp;
    } else {
      mosTemp = ByteUtil.toInt8(bytes[100] > 70 ? 70 + (bytes[100] - 70) ~/ 2 : bytes[100]);
    }

    maxSpeed = ByteUtil.toInt16(ByteUtil.bytes2Int16Le(bytes, 135).value.abs());

    remainderRange = Uint16(((batteryLevel.value / 10000) * estimatedTotalMileage.value).toInt());
  }

  /// 根据当前电压计算剩余电量
  /// 单位0.01%
  int calBatteryLevelByVoltage(int voltage) {
    if (voltage >= 8300) return 100 * 100;
    if (voltage <= 6600) return 0;
    return (((voltage - 6600) / (8300 - 6600)) * 100 * 100).toInt();
  }

  /// 计算动态限速
  /// 根据当前型号支持的最大速度[supportedMaxSpeed]和当前电压[voltage]
  /// [supportedMaxSpeed]单位应为0.01km/h
  int calDynamicSpeedLimit(int supportedMaxSpeed, int voltage) {
    var b = voltage / 7670;
    return min((supportedMaxSpeed * (b * b)).toInt(), supportedMaxSpeed);
  }

  void updateBySlowInfoBytes(Uint8ClampedList bytes) {
    Uint16 speedLimit = ByteUtil.toUint16(ByteUtil.bytes2IntLe(bytes, 60, 4) ~/ 10);
    if (dynamicSpeedLimit.value > speedLimit.value) {
      dynamicSpeedLimit = speedLimit;
    }
  }
}

class V10LikeRealTimeError extends AbsRealTimeError {
  Uint4 phraseCurrentSamplingState = Uint4.zero; // 相电流传感器 - 主板
  Uint4 motorHallState = Uint4.zero; // 电机霍尔 - 电机
  Uint4 sensorOutputState = Uint4.zero; // 传感器 - 主板
  Uint4 comState = Uint4.zero; // 通讯错误 - 主板
  Uint4 batteryState = Uint4.zero; // 电池 - 电池
  Uint4 rtcState = Uint4.zero; // RTC - RTC
  Uint4 spiFlashState = Uint4.zero; // 外部存储器 - 主板

  V10LikeRealTimeError.fromBytes(Uint8ClampedList bytes, [int offset = 0]) : super.fromBytes(bytes, offset) {
    originalValue = ByteUtil.bytes2Uint32Le(bytes, 96);
    int v = originalValue.value;
    phraseCurrentSamplingState = ByteUtil.bool2Uint4(v & 0x1 == 0x1);
    motorHallState = ByteUtil.bool2Uint4(v & 0x2 == 0x2);
    sensorOutputState = ByteUtil.bool2Uint4(v & 0x4 == 0x4);
    comState = ByteUtil.bool2Uint4(v & 0x10000 == 0x10000);
    batteryState = ByteUtil.bool2Uint4(v & 0x20000 == 0x20000);
    rtcState = ByteUtil.bool2Uint4(v & 0x40000 == 0x40000);
    spiFlashState = ByteUtil.bool2Uint4(v & 0x80000 == 0x80000);
  }

  bool hasVisibleError() {
    return phraseCurrentSamplingState.value == 1 ||
        motorHallState.value == 1 ||
        sensorOutputState.value == 1 ||
        comState.value == 1 ||
        batteryState.value == 1 ||
        rtcState.value == 1 ||
        spiFlashState.value == 1;
  }
}
